/*
Promise的几大特点:
1.Promise是一个类，每次创建的时候里面传入一个执行器executor,
该执行器会立即执行
2.执行器中有两个参数,resolve,reject分别为两个函数，这两个函数的作用都是改变Promise的状态
resolve--请求成功, reject--请求失败
3.Promise中有三种状态:请求中pending,请求成功fulfilled,请求失败rejected，且状态一经改变后，不能再变化
即：从pending-->fulfilled或者pending-->rejected,起始的状态为pending
4.Promise实例有一个then方法,其内部是判断状态，如果状态成功，则执行成功后的回调successCallback,如果状态成功，则执行成功后的回调successCallback
如果状态失败，就执行失败后的回调failCallback,then方法是被定义在
原则对象上
5.then方法中成功回调会接收成功后的值，失败回调会接收失败后的原因
6.then方法可以被多次调用和链式调用，即它返回的也是一个promise对象,且then方法的参数是由上一个then方法的返回值确定的
*/

const { reject } = require("lodash")

// 将Promise中的三种状态分别存储在三个常量中
const PENDING = 'pending',
	FULFILLED = 'fulfilled',
	REJECTED = 'rejected'
class MyPromise {
	constructor(executor) {
		try {
			executor(this.resolve, this.reject)
		} catch (e) {
			this.reject(e)
		}
	}
	// 将状态值保存在status属性中
	status = PENDING
	// 成功后的值
	value = undefined
	// 失败后的原因
	reason = undefined
	// 成功回调
	successCallback = []
	// 失败回调
	failCallback = []
	resolve = value => {
		// 判断状态值是否发生改变
		if (this.status !== PENDNG) return
		// 将状态值更改为resolved
		this.status = FULFILLED
		this.vaule = value
		// 异步情况下，则执行此代码
		while (this.successCallback.length) this.successCallback.shift()()
	}
	reject = reason => {
		// 判断状态值是否发生改变
		if (this.status !== PENDNG) return
		// 将状态值更改为resolved
		this.status = REJECTED
		this.reason = reason
		// 异步情况下，则执行此代码
		this.failCallback(this.reason)
		while (this.failCallback.length) this.failCallback.shift()()
	}
	// then方法是定义在原型对象上的
	then(successCallback, failCallback) {
		successCallback ? successCallback : value => value
		failCallback ? failCallback : reason => { throw reason }
		let promise2 = new MyPromise((resolve, reject) => {
			// 如果状态成功，则执行成功后的回调函数
			if (this.status === FULFILLED) {
				// 因为获取在内部获取不到promise2这个参数，所以加入异步代码
				setTimeout(() => {
					try {
						let x = successCallback(this.value)
						resolvePromise(promise2, x, resolve, reject)
					} catch (e) {
						reject(e)
					}
				}, 0)
			} else if (this.status === REJECTED) {
				// 状态失败执行失败回调
				setTimeout(() => {
					try {
						let x = failCallback(this.reason)
						resolvePromise(promise2, x, resolve, reject)
					} catch (e) {
						reject(e)
					}
				}, 0)
			} else {
				// 异步情况下，状态还是在等待的情况下，先将成功回调与失败回调存储起来
				this.successCallback.push(() => {
					setTimeout(() => {
						try {
							let x = successCallback(this.value)
							resolvePromise(promise2, x, resolve, reject)
						} catch (e) {
							reject(e)
						}
					}, 0)
				})
				this.failCallback.push(() => {
					setTimeout(() => {
						try {
							let x = failCallback(this.reason)
							resolvePromise(promise2, x, resolve, reject)
						} catch (e) {
							reject(e)
						}
					}, 0)
				})
			}
		})
		return promise2
	}
	// Promise.all是静态方法要用"static"关键字
	static all(array) {
		// Promise.all接收的是一个数组，返回的是一个Promise对象,且将接收数组的结果按照顺序放在数组中传递给then的成功回调
		let result = []
		let index = 0
		return new MyPromise((resolve, reject) => {
			function addData(key, value) {
				result[key] = value
				index++
				// 出现异步代码时，判断index长度是否与array.length值相等
				if (index === array.length) {
					resolve(result)
				}
			}
			// 对数组array中的每个元素进行遍历
			for (let i = 0; i < array.length; i++) {
				let current = array[i]
				// 判断current是否为promise对象
				if (current instanceof MyPromise) {
					// Promise对象
					current.then(value => addData(i, value), reason => reject(reason))
				} else {
					// 普通值
					addData(i, aray[i])
				}
			}
		})
	}
	// Promise.resolve方法也是静态方法，返回的也是一个Promise对象
	static resolve(value) {
		// 判断value是否为Promise对象，如果是则直接返回这个对象，如果不是则将值包裹成一个Promise对象并将值进行返回
		if (value instanceof MyPromise) return value
		return new MyPromise(resolve => resolve(value))
	}
	// finally方法是无论成功或者失败都会执行,在finally后面依然可以链式调用then方法拿到最终返回的结果
	finally(callback) {
		return this.then(value => {
			// 不管返回的值是普通值还是promise都将其包裹在promise对象中
			return MyPromise.resolve(callback()).then(() => value)
		}, reason => {
			return MyPromise.resolve(callback()).then(() => { throw reason })
		})
	}

	// catch方法返回的也是一个promise对象，而且是捕获错误的
	catch(failCallback) {
		return this.then(undefined, failCallback)	
	}

}
// 封装一个函数对其进行处理，x的值有可能是Promise对象，有可能是普通值，而且不能返回它本身
function resolvePromise(promise2, x, resolve, reject) {
	// 判断x的值是否与它本身相等
	if (promise2 === x) return reject(new TypeError('Chaining cycle detected for promise #<Promise>'))
	if (x instanceof MyPromise) {
		x.then(resolve, reject)
	} else {
		resolve(x)
	}
}